package com.huiyeji.dynamic.core;



import com.huiyeji.dynamic.core.annotations.DynamicBean;
import com.huiyeji.dynamic.core.annotations.DynamicMethod;
import com.huiyeji.dynamic.core.annotations.Private;
import com.huiyeji.dynamic.core.builder.*;
import com.huiyeji.dynamic.core.handler.ParserMatchFailureHandler;
import com.huiyeji.dynamic.core.parser.DefaultDynamicStatementParser;
import com.huiyeji.dynamic.core.parser.DirectAssignmentParser;
import com.huiyeji.dynamic.core.parser.DynamicParser;
import com.huiyeji.dynamic.core.parser.IncrementDecrementParser;
import com.huiyeji.sort.MatchRule;
import com.huiyeji.sort.ObjectMatcher;
import com.huiyeji.sort.Order;

import java.util.*;

/**
 * @author Huiyeji
 */
@DynamicBean("context")
public class DynamicContext {

    private static final MatchRule<String,RegexContainer> RULE = new RegexContainerRule();

    private static final DynamicExecutor EXECUTOR = new DefaultDynamicStatementExecutor();

    private static final ObjectMatcher<String, RegexContainer,DynamicParser> DYNAMIC_PARSER_MATCHER;

    private static final ObjectMatcher<String,RegexContainer, ParamProviderBuilder> PARAM_PROVIDER_BUILDER_MATCHER;

    private final Map<String, DynamicInterceptor> DYNAMIC_INTERCEPTOR_MAP = new HashMap<String, DynamicInterceptor>();


    static {
        //注册DynamicParser匹配器
        DYNAMIC_PARSER_MATCHER = new ObjectMatcher<>(RULE, new ParserMatchFailureHandler());
        DYNAMIC_PARSER_MATCHER.registerObject(
                new DefaultDynamicStatementParser(),
                new DirectAssignmentParser(),
                new IncrementDecrementParser()
        );

        //注册ParamProviderBuilder匹配器
        PARAM_PROVIDER_BUILDER_MATCHER = new ObjectMatcher<>(RULE);
        PARAM_PROVIDER_BUILDER_MATCHER.registerObject(
                new StringParamProviderBuilder(),
                new IntegerParamProviderBuilder(),
                new BooleanParamProviderBuilder(),
                new NullParamProviderBuilder(),
                new ContextParamProviderBuilder(),
                new StatementParamProviderBuilder(),
                new IntegerCalculationParamProviderBuilder()
        );
    }

    private final DynamicObjectFactory factory;

    private final DynamicPluginManager pluginManager;

    private final ObjectMatcher<String, RegexContainer,DynamicParser> dynamicParserMatcher = DYNAMIC_PARSER_MATCHER;

    private final ObjectMatcher<String,RegexContainer, ParamProviderBuilder> paramProviderBuilderMatcher = PARAM_PROVIDER_BUILDER_MATCHER;

    private final DynamicExecutor executor = EXECUTOR;

    private DynamicStatementPointer pointer;

    private DynamicMonitor monitor;

    private final Map<String, DynamicInterceptor> dynamicInterceptorMap = DYNAMIC_INTERCEPTOR_MAP;

    private final Map<String, ContextObjectItem> contextMap = new HashMap<String, ContextObjectItem>();

    private final Map<String, Object> returnMap = new HashMap<String, Object>();

    public DynamicContext(Map<String,Object> globalContextMap,DynamicObjectFactory factory,DynamicPluginManager pluginManager) {
        this.factory = factory;
        if (pluginManager == null){
            this.pluginManager = new DynamicPluginManager(this);
        } else {
            this.pluginManager = pluginManager;
        }
        this.initEnvironment(globalContextMap);
    }

    public DynamicContext(Map<String,Object> globalContextMap,DynamicObjectFactory factory) {
        this(globalContextMap,factory,null);
    }

    private void initEnvironment(Map<String,Object> globalContextMap){
        //全局常量初始化
        if (globalContextMap != null){
            for (Map.Entry<String, Object> entry : globalContextMap.entrySet()) {
                contextMap.put(entry.getKey(), new ContextObjectItem(entry.getValue(),true));
            }
        }

        //注册monitor
        this.monitor = new DynamicMonitor(this);

        //注册context动态对象
        this.factory.registerDynamicContext(this);
    }

    @Private
    public ObjectMatcher<String, RegexContainer,DynamicParser> getParserMatcher() {
        return this.dynamicParserMatcher;
    }

    @Private
    public ObjectMatcher<String, RegexContainer,ParamProviderBuilder> getParamProviderBuilderMatcher() {
        return this.paramProviderBuilderMatcher;
    }

    @Private
    public DynamicExecutor getExecutor() {
        return executor;
    }

    @Private
    public DynamicObjectFactory getFactory() {
        return factory;
    }

    @Private
    public DynamicPluginManager getPluginManager(){
        return this.pluginManager;
    }

    @Private
    public DynamicMonitor getMonitor() {
        return monitor;
    }

    @DynamicMethod("get")
    public Object getObject(String key) {
        ContextObjectItem item = contextMap.get(key);
        if (item == null) {
            return null;
        }
        return item.getObject();
    }

    @DynamicMethod("set")
    public void setObject(String key, Object value) {
        if (contextMap.containsKey(key)) {
            ContextObjectItem contextObjectItem = contextMap.get(key);
            if (contextObjectItem.isGlobal()){
                throw new RuntimeException("context item 【"+key+"】 is global (read only) , update error !");
            }
            contextObjectItem.setObject(value);
        }else {
            contextMap.put(key, new ContextObjectItem(value,false));
        }
    }

    @DynamicMethod("remove")
    public void removeObject(String key){
        contextMap.remove(key);
    }

    @Order(1)
    @DynamicMethod("return")
    public void returnObject(String objectName){
        returnMap.put(objectName,getObject(objectName));
    }

    @Order(10)
    @DynamicMethod("return")
    public void returnObject(String key,Object value){
        returnMap.put(key,value);
    }

    @Private
    protected void _globalization(){
        for (Map.Entry<String, ContextObjectItem> entry : contextMap.entrySet()) {
            entry.getValue().setGlobal(true);
        }
    }

    @Private
    protected void setPointer(DynamicStatementPointer pointer){
        this.pointer = pointer;
    }

    @Private
    public DynamicStatementPointer getPointer(){
        return this.pointer;
    }

    @Private
    public void registerDynamicInterceptor(String name,DynamicInterceptor interceptor){
        dynamicInterceptorMap.put(name,interceptor);
    }

    @Private
    public void removeInterceptor(String name){
        dynamicInterceptorMap.remove(name);
    }

    @Private
    public DynamicInterceptor getInterceptor(String name){
        return dynamicInterceptorMap.get(name);
    }

    @Private
    public Collection<DynamicInterceptor> getInterceptors(){
        return dynamicInterceptorMap.values();
    }

    @Private
    protected void tempLoad(Map<String,Object> tempData){
        for (Map.Entry<String, Object> entry : tempData.entrySet()) {
            setObject(entry.getKey(), entry.getValue());
        }
    }

    @Private
    protected Map<String, Object> getContextData() {
        Map<String, Object> data = new HashMap<String, Object>();
        for (Map.Entry<String, ContextObjectItem> entry : contextMap.entrySet()) {
            data.put(entry.getKey(),entry.getValue().getObject());
        }
        return data;
    }

    @Private
    protected Map<String, Object> getGlobalContextData() {
        Map<String, Object> data = new HashMap<String, Object>();
        for (Map.Entry<String, ContextObjectItem> entry : contextMap.entrySet()) {
            if (entry.getValue().isGlobal()){
                data.put(entry.getKey(),entry.getValue().getObject());
            }
        }
        return data;
    }

    @Private
    protected Map<String,Object> getReturnData() {
        return returnMap;
    }

    @Private
    protected void tempClear(boolean clearPointer) {
        Iterator<Map.Entry<String, ContextObjectItem>> iterator = contextMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<String, ContextObjectItem> entry = iterator.next();
            if (!entry.getValue().isGlobal()){
                iterator.remove();
            }
        }

        returnMap.clear();

        monitor.tempClear();

        factory.tempClear();
        if (clearPointer){
            setPointer(null);
        } else {
            this.pointer.reset();
        }
    }
}
